/*
* Copyright (c) 2003, the JUNG Project and the Regents of the University
* of California
* All rights reserved.
*
* This software is open-source under the BSD license; see either
* "license.txt" or
* http://jung.sourceforge.net/license.txt for a description.
*/
package edu.uci.ics.jung.algorithms.generators.random;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.apache.commons.collections15.Factory;

import edu.uci.ics.jung.algorithms.generators.GraphGenerator;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.UndirectedGraph;

/**
 * Generates a random graph using the Erdos-Renyi binomial model (each pair of
 * vertices is connected with probability p).
 * 
 * @author William Giordano, Scott White, Joshua O'Madadhain
 */
public class ErdosRenyiGenerator<V, E> implements GraphGenerator<V, E> {
	private int mNumVertices;
	private double mEdgeConnectionProbability;
	private Random mRandom;
	Factory<UndirectedGraph<V, E>> graphFactory;
	Factory<V> vertexFactory;
	Factory<E> edgeFactory;

	/**
	 *
	 * @param numVertices
	 *            number of vertices graph should have
	 * @param p
	 *            Connection's probability between 2 vertices
	 */
	public ErdosRenyiGenerator(Factory<UndirectedGraph<V, E>> graphFactory,
			Factory<V> vertexFactory, Factory<E> edgeFactory, int numVertices,
			double p) {
		if (numVertices <= 0) {
			throw new IllegalArgumentException(
					"A positive # of vertices must be specified.");
		}
		mNumVertices = numVertices;
		if (p < 0 || p > 1) {
			throw new IllegalArgumentException("p must be between 0 and 1.");
		}
		this.graphFactory = graphFactory;
		this.vertexFactory = vertexFactory;
		this.edgeFactory = edgeFactory;
		mEdgeConnectionProbability = p;
		mRandom = new Random();
	}

	/**
	 * Returns a graph in which each pair of vertices is connected by an
	 * undirected edge with the probability specified by the constructor.
	 */
	@Override
	public Graph<V, E> create() {
		UndirectedGraph<V, E> g = graphFactory.create();
		for (int i = 0; i < mNumVertices; i++) {
			g.addVertex(vertexFactory.create());
		}
		List<V> list = new ArrayList<V>(g.getVertices());

		for (int i = 0; i < mNumVertices - 1; i++) {
			V v_i = list.get(i);
			for (int j = i + 1; j < mNumVertices; j++) {
				V v_j = list.get(j);
				if (mRandom.nextDouble() < mEdgeConnectionProbability) {
					g.addEdge(edgeFactory.create(), v_i, v_j);
				}
			}
		}
		return g;
	}

	/**
	 * Sets the seed of the internal random number generator to {@code seed}.
	 * Enables consistent behavior.
	 */
	public void setSeed(long seed) {
		mRandom.setSeed(seed);
	}
}
